package com.spark.gaea.security.config;

import com.google.common.base.Preconditions;
import com.spark.gaea.config.service.easyzk.ConfigNode;
import com.spark.gaea.security.backend.SecurityBackend;
import com.spark.gaea.security.cache.RedisCacheManager;
import com.spark.gaea.security.cache.RedisManager;
import com.spark.gaea.security.cache.RedisSessionDAO;
import com.spark.gaea.security.eis.SessionKeyGenerator;
import com.spark.gaea.security.matcher.GaeaCredentialsMatcher;
import com.spark.gaea.security.realm.GaeaRealm;
import com.spark.gaea.utils.StringUtil;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Scope;
import org.springframework.core.env.Environment;

import java.util.concurrent.TimeUnit;

@Configuration
@Import({LifecycleConfig.class})
public abstract class AbstractShiroConfig {
	private final Logger LOG = LoggerFactory.getLogger(this.getClass());
	@Autowired
	private Environment env;
	@Autowired
	@Qualifier("applicationConfigNode")
	private ConfigNode applicationConfigNode;
	private static final String DEFAULT_PORT = "6379";
	private static final String REDIS_MANAGER_EXPIRE_CONFIG = "shiro.redisManager.expire";
	private static final String DEFAULT_REDIS_MANAGER_EXPIRE = "1800";
	private ShiroFilterFactoryBean shiroFilter;

	public abstract SecurityBackend securityBackend();

	@Bean
	public RedisManager redisManager() {
		final ConfigNode configNode = this.applicationConfigNode;
		final String host = Preconditions.checkNotNull(StringUtil.getProperty(configNode, this.env, "shiro.redis.host"),
				"shiro.redis.host is null");
		final int port = Integer.parseInt(StringUtil.getProperty(configNode, this.env, "shiro.redis.port", "6379"));
		final int expire = Integer
				.parseInt(StringUtil.getProperty(configNode, this.env, "shiro.redisManager.expire", "1800"));
		final RedisManager redisManager = new RedisManager();
		redisManager.setHost(host);
		redisManager.setPort(port);
		redisManager.setExpire(expire);
		return redisManager;
	}

	@Bean
	public CacheManager cacheManager() {
		final RedisCacheManager cacheManager = new RedisCacheManager();
		cacheManager.setRedisManager(this.redisManager());
		return cacheManager;
	}

	@Bean
	public RedisSessionDAO redisSessionDAO() {
		final RedisSessionDAO sessionDAO = new RedisSessionDAO();
		sessionDAO.setRedisManager(this.redisManager());
		sessionDAO.setSessionIdGenerator(new SessionKeyGenerator());
		return sessionDAO;
	}

	@Bean
	public SessionManager sessionManager() {
		final ConfigNode configNode = this.applicationConfigNode;
		final int expire = Integer
				.parseInt(StringUtil.getProperty(configNode, this.env, "shiro.redisManager.expire", "1800"));
		final DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		sessionManager.setSessionDAO(this.redisSessionDAO());
		final SimpleCookie cookie = new SimpleCookie("sessionKey");
		cookie.setHttpOnly(false);
		cookie.setPath("/");
		sessionManager.setSessionIdCookie(cookie);
		sessionManager.setGlobalSessionTimeout(expire * TimeUnit.SECONDS.toMillis(1L));
		sessionManager.setSessionValidationInterval(expire * TimeUnit.SECONDS.toMillis(1L));
		return sessionManager;
	}

	@Bean
	public SimpleCredentialsMatcher sha512Matcher() {
		final GaeaCredentialsMatcher customerMatcher = new GaeaCredentialsMatcher();
		return customerMatcher;
	}

	@Bean
	public AuthorizingRealm authorizingRealm() {
		final AuthorizingRealm authorizingRealm = new GaeaRealm();
		authorizingRealm.setCredentialsMatcher(this.sha512Matcher());
		return authorizingRealm;
	}

	@Bean
	public DefaultWebSecurityManager securityManager() {
		final DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(this.authorizingRealm());
		securityManager.setCacheManager(this.cacheManager());
		securityManager.setSessionManager(this.sessionManager());
		return securityManager;
	}

	@Bean
	@Scope("prototype")
	public ShiroFilterFactoryBean shiroFilter() {
		if (this.shiroFilter != null) {
			return this.shiroFilter;
		}
		(this.shiroFilter = new ShiroFilterFactoryBean()).setSecurityManager(this.securityManager());
		return this.shiroFilter;
	}
}
